#pragma once
#include "BoonException.h"



// --------------- IMPLEMENTATION Shared_ptr ----------->
namespace PimplImplementation
{

template<typename T>
class Weak_ptr_Impl;

template<typename T>
class Shared_ptr_Impl
{
public:

	Shared_ptr_Impl()
		: m_Object( nullptr ),
		  m_Ref( nullptr )
	{

	}

	Shared_ptr_Impl( const Shared_ptr_Impl<T>& o )
		: m_Object( o.m_Object ),
		  m_Ref( o.m_Ref )
	{
		if( m_Ref ) m_Ref->Increase();
	}

	template<typename Y> // conversion ctor
	Shared_ptr_Impl( const Shared_ptr_Impl<Y>& o )
		: m_Object( static_cast<T*>( o.m_Object ) ),
		  m_Ref( reinterpret_cast<RefCount*>( o.m_Ref ) )
	{
		if( m_Ref ) m_Ref->Increase();
	}

	Shared_ptr_Impl( const Weak_ptr_Impl<T>& o )
		: m_Object( o.m_Object ),
		  m_Ref( o.m_Ref )
	{
		if( !m_Object )
			throw( Boon::BoonException( "Shared_ptr_impl_own",
										"Shared_ptr_Impl( const Weak_ptr_Impl<T>& o )",
										"Cannot create Shared_ptr from Weak_ptr containing null-reference!" ) );
		if( m_Ref ) m_Ref->Increase();
	}

	Shared_ptr_Impl( T* o )
		: m_Ref( new RefCount() ),
		  m_Object( o )
	{
		m_Ref->Increase();
	}

	~Shared_ptr_Impl()
	{
		Release();
	}

	inline void Release()
	{
		// Releasing memory of members if destroying last instance
		// of Shared_ptr holding this instance (m_Object)
		if( m_Ref && m_Ref->Decrease() == 0 )
		{
			// scope lock
			//boost::lock_guard( m_mutex );
			delete m_Ref;
			if( m_Object ) delete m_Object;
		}
		m_Object = nullptr;
		m_Ref = nullptr;
	}

	inline T* Get() const
	{
		return m_Object;
	}


	inline T& operator*() const
	{
		return *m_Object;
	}

	friend void swap( Shared_ptr_Impl<T>& lhs, Shared_ptr_Impl<T>& rhs ) 
	{
		// enable ADL (not necessary in our case, but good practice)
		using std::swap; 

		// by swapping the members of two classes,
		// the two classes are effectively swapped
		swap( lhs.m_Ref, rhs.m_Ref );
		swap( lhs.m_Object, rhs.m_Object ); 
	}

	inline const Shared_ptr_Impl<T>& operator=( Shared_ptr_Impl<T> rhs )
	{
		//check self assignment
		swap( *this, rhs );
		return *this;
	}

	inline bool operator ==( const Shared_ptr_Impl<T>& rhs ) const
	{
		return ( m_Object == rhs.m_Object );
	}

	inline bool operator !=( const Shared_ptr_Impl<T>& rhs ) const
	{
		return ( m_Object != rhs.m_Object );
	}

	template<typename N>
	inline operator Shared_ptr_Impl<N>() const
	{
		Shared_ptr_Impl<N> n;
		n.m_Object = m_Object;
		n.m_Ref = reinterpret_cast<Shared_ptr_Impl<N>::RefCount*>( m_Ref );
		n.m_Ref->Increase();
		return n;
	}

	inline unsigned int UseCount() const
	{
		if( m_Ref )
			return m_Ref->GetCount();
		return 0;
	}


private:

	class						RefCount;		// Fwd
	T*							m_Object;		// Object to be stored
	RefCount*					m_Ref;			// Reference counting object holding information about quantity of instances


	template<typename T>
	friend class Weak_ptr_Impl;
	template<typename Y>
	friend class Shared_ptr_Impl;

	// Reference-Counting-Class containing logic about
	// increasing decreasing a reference counter
	class RefCount
	{
	public:
		typedef std::list<T**> ListWeakObjectRef;

		RefCount() : m_Count( 0 ) { }
		RefCount( const RefCount& o ) : m_Count( o.m_Count ) {}
		~RefCount()
		{
			auto i = m_WeakObjectReferences.begin();
			auto e = m_WeakObjectReferences.end();

			// set all m_Objects (referenced objects) of registered
			// weak_ptrs to nullptr; that will make them Expired() == true
			for( ; i != e; ++i )
				*(*i) = nullptr;

			m_WeakObjectReferences.clear();
		}

		/// <summary>
		/// Utils::Shared_ptr::RefCount<T>::Increase - Increasing counter by 1
		/// </summary>
		/// <returns>int - Representing the current reference count</returns>
		inline int Increase()
		{
			return ++m_Count;
		}

		/// <summary>
		/// Utils::Shared_ptr::RefCount<T>::Decrease - Decreasing counter by 1
		/// </summary>
		/// <returns>int - Representing the current reference count</returns>
		inline int Decrease()
		{
			//scoped lock
			if( m_Count > 0 ) return --m_Count;
			return 0;
		}

		/// <summary>
		/// Utils::Shared_ptr::RefCount<T>::GetCount - Get counter of references
		/// </summary>
		/// <returns>int - Representing the current reference count</returns>
		inline int GetCount() const
		{
			return m_Count;
		}

		/*! \brief		Adds the observed object-reference of a weak_ptr to list, that will be set to null
						if strong reference has been expired
			\param		referencedObject - Po
			\param		outListPos - Will write the iterator onto this passed iterator object, will be used
						to later remove the object-reference from this list in constant time
		*/
		inline void RegisterObservedObjectPtr( T** referencedObject, typename ListWeakObjectRef::iterator& outListPos )
		{

			m_WeakObjectReferences.push_front( referencedObject );
			outListPos = m_WeakObjectReferences.begin();
		}

		/*! \brief		Removes a observed ObjectPtr from list, should be called if weak_ptr
						changing owner( ctor, dtor, operator= )
			\param		weakIterPos - Position in list fetched earlier by RegisterObservedObjectPtr()
		*/
		inline void RemoveObservedObjectPtr( const typename ListWeakObjectRef::iterator& weakIterPos )
		{
			m_WeakObjectReferences.erase( weakIterPos );
		}

	private:
		int					m_Count;	// Number of instances
		ListWeakObjectRef	m_WeakObjectReferences;
	};
};

}
